package org.codehaus.activemq.util;

import java.io.PrintStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.jms.Connection;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.naming.NamingException;
import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.Level;
import org.apache.log4j.spi.ErrorHandler;
import org.apache.log4j.spi.LoggingEvent;

public abstract class JmsLogAppenderSupport extends AppenderSkeleton
{
  public static final int JMS_PUBLISH_ERROR_CODE = 61616;
  private Connection connection;
  private Session session;
  private MessageProducer producer;
  private boolean allowTextMessages = true;
  private String subjectPrefix = "log4j.";

  public Connection getConnection()
    throws JMSException, NamingException
  {
    if (this.connection == null) {
      this.connection = createConnection();
    }
    return this.connection;
  }

  public void setConnection(Connection connection) {
    this.connection = connection;
  }

  public Session getSession() throws JMSException, NamingException {
    if (this.session == null) {
      this.session = createSession();
    }
    return this.session;
  }

  public void setSession(Session session) {
    this.session = session;
  }

  public MessageProducer getProducer() throws JMSException, NamingException {
    if (this.producer == null) {
      this.producer = createProducer();
    }
    return this.producer;
  }

  public void setProducer(MessageProducer producer) {
    this.producer = producer;
  }

  public void close() {
    List errors = new ArrayList();
    if (this.producer != null) {
      try {
        this.producer.close();
      }
      catch (JMSException e) {
        errors.add(e);
      }
    }
    if (this.session != null) {
      try {
        this.session.close();
      }
      catch (JMSException e) {
        errors.add(e);
      }
    }
    if (this.connection != null) {
      try {
        this.connection.close();
      }
      catch (JMSException e) {
        errors.add(e);
      }
    }
    for (Iterator iter = errors.iterator(); iter.hasNext(); ) {
      JMSException e = (JMSException)iter.next();
      getErrorHandler().error("Error closing JMS resources: " + e, e, 61616);
    }
  }

  public boolean requiresLayout() {
    return false;
  }

  public void activateOptions()
  {
    try {
      getProducer();
    }
    catch (Exception e) {
      getErrorHandler().error("Could not create JMS resources: " + e, e, 61616);
    }
  }

  protected abstract Connection createConnection()
    throws JMSException, NamingException;

  protected Session createSession() throws JMSException, NamingException
  {
    return getConnection().createSession(false, 1);
  }

  protected MessageProducer createProducer() throws JMSException, NamingException {
    return getSession().createProducer(null);
  }

  protected void append(LoggingEvent event) {
    System.out.println("#### Calling append with event: " + event);
    try {
      Message message = createMessage(event);
      Destination destination = getDestination(event);
      getProducer().send(destination, message);
    }
    catch (Exception e) {
      getErrorHandler().error("Could not send message due to: " + e, e, 61616, event);
    }
  }

  protected Message createMessage(LoggingEvent event) throws JMSException, NamingException {
    Message answer = null;
    Object value = event.getMessage();
    if ((this.allowTextMessages) && ((value instanceof String))) {
      answer = getSession().createTextMessage((String)value);
    }
    else {
      answer = getSession().createObjectMessage((Serializable)value);
    }
    answer.setStringProperty("level", event.getLevel().toString());
    answer.setIntProperty("levelInt", event.getLevel().toInt());
    answer.setStringProperty("threadName", event.getThreadName());
    return answer;
  }

  protected Destination getDestination(LoggingEvent event) throws JMSException, NamingException {
    String name = this.subjectPrefix + event.getLoggerName();
    return getSession().createTopic(name);
  }
}